#include "bitarray.h"
#include <fstream>

void BitArray::Init(uint64 size, uint64 init_pattern) {
  CHECK(!overlay_);
  delete [] block_.data_;
  uint64 word_size = RoundUpToMultipleOf(size, 64) / 64;
  block_ = 
    Memblock((char *)(new uint64[word_size + 1]), 
	     (word_size + 1) * sizeof(uint64));
  Size() = size;
  for (uint64 i=0; i<word_size; i++) Data()[i] = init_pattern;
  cout << "inited bitarray -  size = " << Size() 
       << " word_size = " << word_size << endl;
}

void BitArray::Store(string fn) {
  CHECK(!overlay_);
  File::Write(fn, block_); }

void BitArray::Load(string fn) {
  CHECK(!overlay_);
  delete [] block_.data_;
  block_ = File::Read(fn); 
  // cout << "Load BitArray memblock size:" << block_.size_ << endl;
}

void BitArray::Commit() {
  CHECK(overlay_);
  CHECK(!overlay_->base_);
  const HashTable & h = overlay_->h_;
  for (uint64 i=0; i<h.size_; i++) {
    const HashTable::Cell & c = h.data_[i];
    if (h.data_[i].IsValid()) base_->Data()[c.key_] = c.value_;
  }
  overlay_->Clear();
}

bool BitArray::Get(uint64 i) {
  uint64 w = ReadableWord(i >> 6);
  return w & (1ull << (i & 0x3F)); }

void BitArray::Set(uint64 i, bool val) {
  uint64 *wp = WritableWordPointer(i >> 6);
  uint64 bit = (1ull << (i & 0x3f));
  uint64 vbit = val ? bit : 0;
  *wp = vbit | (*wp & ~bit); }


void BitArray::OrEqRange(uint64 start, uint64 length, uint64 bits) {
  uint64 w = start >> 6;
  uint64 p = start & 0x3F;
  // TODO: make faster
  *WritableWordPointer(w) |= bits << p;
  if (p + length > 64)
    *WritableWordPointer(w+1) |= bits >> (64 - p);
  
  //  for (uint64 i=0; i<length; i++) 
  //  Set(start+i, (bits >> i) & 1);
}
uint64 BitArray::GetRange(uint64 start, uint64 length) {
  uint64 w = start >> 6;
  uint64 p = start & 0x3F;
  return  
    (
     (ReadableWord(w) >> p) 
     + ( (p + length > 64) ? (ReadableWord(w + 1) << (64-p)) : 0)
     ) & ((1ull << length) - 1);
  // TODO: make faster
  //uint64 ret = 0;
  //for (uint64 i=0; i<length; i++) 
  //  ret += ((uint64)Get(start+i)) << i;
  //return ret;
}

uint64 BitArray::ReadableWord(uint64 i) {
  if (overlay_) {
    const uint64 * p = *overlay_ % i;
    if (p) return *p;
    return base_->ReadableWord(i); }
  return Data()[i]; }

uint64 *BitArray::WritableWordPointer(uint64 i) {
  bool inserted;
  if (overlay_) {
    uint64 * look
      = overlay_->LookupWritable(i, &inserted);
    if (inserted) *look = base_->ReadableWord(i);
    return look;
  }
  return Data() + i; }

RNG bitarrayrng;

void BitArray::AddBase(BitArray *base, Table<uint64> *overlay) {
  CHECK(!block_.data_);
  overlay_ = overlay;
  overlay_->Clear();
  base_ = base;
}

uint64& BitArray::Size() {
  if (base_) return base_->Size();
  return ((uint64 *)block_.data_)[0]; }

void SimpleBitArray::Init(uint64 size) {
  delete [] block_.data_;
  uint64 word_size = CeilingDiv(size, 64);
  block_ = 
    Memblock((char *)(new uint64[word_size + 1]), 
	     (word_size + 1) * sizeof(uint64));
  Size() = size;
  data_ = Data();
  cout << "inited bitarray -  size = " << Size() 
       << " word_size = " << word_size << endl;
}

void SimpleBitArray::ZeroOut() {
  for (uint64 i=0; i<WordSize(); i++) data_[i] = 0;
}

void SimpleBitArray::Store(string fn) {
  Memblock writable_block(block_.begin(), 
			  sizeof(uint64) * (1 + WordSize()));
  File::Write(fn, writable_block); 
}

void SimpleBitArray::Load(string fn) {
  delete [] block_.data_;
  block_ = File::Read(fn);
  data_ = Data();
  cout << "Load SimpleBitArray memblock size:" << block_.size_ << endl;
}
